//
//  NSArrayAdditions.m
//  Tint
//
//  Created by Kevin Wu on 2/22/12.
//  Copyright (c) 2012 Telligenty. All rights reserved.
//

#import "NSArrayAdditions.h"
#import "NSObjectAdditions.h"
#import "NSMutableArrayAdditions.h"

@implementation NSArray (Tint)


#pragma mark - Querying

- (id)objectOrNilAtIndex:(NSUInteger)i {
    if (i < [self count]) {
        return [self objectAtIndex:i];
    }
    return nil;
}

- (id)firstObject {
    return [self objectOrNilAtIndex:0];
}

- (id)randomObject {
    if ([self count] > 0) {
        int i = arc4random() % [self count];
        return [self objectAtIndex:i];
    }
    return nil;
}



#pragma mark - Ordering and filtering

- (NSArray *)shuffledArray {
    NSMutableArray *tmp = [NSMutableArray arrayWithArray:self];
    [tmp shuffle];
    return [NSArray arrayWithArray:tmp];
}

- (NSArray *)reversedArray {
    NSMutableArray *tmp = [NSMutableArray arrayWithArray:self];
    [tmp reverse];
    return [NSArray arrayWithArray:tmp];
}

- (NSArray *)nonEqualObjectsArray {
    NSMutableArray *array = [NSMutableArray array];
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [array addNonEqualObjectIfNotNil:object];
    }
    if ([array count]>0)
        return [NSArray arrayWithArray:array];
    
    return nil;
}

- (NSArray *)nonIdenticalObjectsArray {
    NSMutableArray *array = [NSMutableArray array];
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [array addNonIdenticalObjectIfNotNil:object];
    }
    if ([array count]>0)
        return [NSArray arrayWithArray:array];
    
    return nil;
}


#pragma mark - KVC

- (CGFloat)avg:(NSString *)keyPath {
    return [[self valueForKeyPath:[NSString stringWithFormat:@"@avg.%@", keyPath]] floatValue];
}

- (CGFloat)max:(NSString *)keyPath {
    return [[self valueForKeyPath:[NSString stringWithFormat:@"@max.%@", keyPath]] floatValue];
}

- (CGFloat)min:(NSString *)keyPath {
    return [[self valueForKeyPath:[NSString stringWithFormat:@"@min.%@", keyPath]] floatValue];
}

- (CGFloat)sum:(NSString *)keyPath {
    return [[self valueForKeyPath:[NSString stringWithFormat:@"@sum.%@", keyPath]] floatValue];
}

- (NSArray *)unionOfObjects:(NSString *)keyPath {
    NSMutableArray *values = [NSMutableArray array];
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [values addObjectIfNotNil:[object valueForKeyPath:keyPath]];
    }
    return [NSArray arrayWithArray:values];
}

- (NSArray *)nonEqualUnionOfObjects:(NSString *)keyPath {
    NSMutableArray *values = [NSMutableArray array];
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [values addNonEqualObjectIfNotNil:[object valueForKeyPath:keyPath]];
    }
    return [NSArray arrayWithArray:values];
}

- (NSArray *)nonIdenticalUnionOfObjects:(NSString *)keyPath {
    NSMutableArray *values = [NSMutableArray array];
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [values addNonIdenticalObjectIfNotNil:[object valueForKeyPath:keyPath]];
    }
    return [NSArray arrayWithArray:values];
}

- (NSArray *)objectsForKeyPath:(NSString *)keyPath equalToValue:(id)value {
    NSMutableArray *objects = [NSMutableArray array];
    for (id object in self) {
        if ([object isValueForKeyPath:keyPath equalToValue:value]) {
            [objects addObject:object];
        }
    }
    return [NSArray arrayWithArray:objects];
}

- (NSArray *)objectsForKeyPath:(NSString *)keyPath identicalToValue:(id)value {
    NSMutableArray *objects = [NSMutableArray array];
    for (id object in self) {
        if ([object isValueForKeyPath:keyPath identicalToValue:value]) {
            [objects addObject:object];
        }
    }
    return [NSArray arrayWithArray:objects];
}

- (id)firstObjectForKeyPath:(NSString *)keyPath equalToValue:(id)value {
    return [[self objectsForKeyPath:keyPath equalToValue:value] firstObject];
}

- (id)firstObjectForKeyPath:(NSString *)keyPath identicalToValue:(id)value {
    return [[self objectsForKeyPath:keyPath identicalToValue:value] firstObject];
}


#pragma mark - Sending messages

- (void)makeObjectsRunSelector:(SEL)selector {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelector:selector];
    }
}

- (void)makeObjectsRunSelector:(SEL)selector withObject:(id)p1 {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelector:selector withObject:p1];
    }
}

- (void)makeObjectsRunSelector:(SEL)selector withObject:(id)p1 withObject:(id)p2 {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelector:selector withObject:p1 withObject:p2];
    }
}

- (void)makeObjectsRunSelector:(SEL)selector withObject:(id)p1 withObject:(id)p2 withObject:(id)p3 {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelector:selector withObject:p1 withObject:p2 withObject:p3];
    }
}

- (void)makeObjectsRunSelector:(SEL)selector withObject:(id)p1 withObject:(id)p2 withObject:(id)p3 withObject:(id)p4 {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelector:selector withObject:p1 withObject:p2 withObject:p3 withObject:p4];
    }
}

- (void)makeObjectsRunSelector:(SEL)selector withObject:(id)p1 withObject:(id)p2 withObject:(id)p3 withObject:(id)p4 withObject:(id)p5 {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelector:selector withObject:p1 withObject:p2 withObject:p3 withObject:p4 withObject:p5];
    }
}

- (void)makeObjectsRunSelector:(SEL)selector withObject:(id)p1 withObject:(id)p2 withObject:(id)p3 withObject:(id)p4 withObject:(id)p5 withObject:(id)p6 {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelector:selector withObject:p1 withObject:p2 withObject:p3 withObject:p4 withObject:p5 withObject:p6];
    }
}



- (void)makeObjectsRunSelectorOnMainThread:(SEL)selector {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelectorOnMainThread:selector];
    }
}

- (void)makeObjectsRunSelectorOnMainThread:(SEL)selector withObject:(id)p1 {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelectorOnMainThread:selector withObject:p1];
    }
}

- (void)makeObjectsRunSelectorOnMainThread:(SEL)selector withObject:(id)p1 withObject:(id)p2 {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelectorOnMainThread:selector withObject:p1 withObject:p2];
    }
}

- (void)makeObjectsRunSelectorOnMainThread:(SEL)selector withObject:(id)p1 withObject:(id)p2 withObject:(id)p3 {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelectorOnMainThread:selector withObject:p1 withObject:p2 withObject:p3];
    }
}

- (void)makeObjectsRunSelectorOnMainThread:(SEL)selector withObject:(id)p1 withObject:(id)p2 withObject:(id)p3 withObject:(id)p4 {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelectorOnMainThread:selector withObject:p1 withObject:p2 withObject:p3 withObject:p4];
    }
}

- (void)makeObjectsRunSelectorOnMainThread:(SEL)selector withObject:(id)p1 withObject:(id)p2 withObject:(id)p3 withObject:(id)p4 withObject:(id)p5 {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelectorOnMainThread:selector withObject:p1 withObject:p2 withObject:p3 withObject:p4 withObject:p5];
    }
}

- (void)makeObjectsRunSelectorOnMainThread:(SEL)selector withObject:(id)p1 withObject:(id)p2 withObject:(id)p3 withObject:(id)p4 withObject:(id)p5 withObject:(id)p6 {
    for (int i=0; i<[self count]; ++i) {
        id object = [self objectAtIndex:i];
        [object runSelectorOnMainThread:selector withObject:p1 withObject:p2 withObject:p3 withObject:p4 withObject:p5 withObject:p6];
    }
}


@end
